import {Component, OnInit, ViewChild, ElementRef, Inject} from '@angular/core';
import {DOCUMENT} from '@angular/common';

import {fromEvent, Subscription} from 'rxjs';
import {Store, select} from '@ngrx/store';

import {AppStoreModule} from '../../../../../store';
import * as PlaySelector from '../../../../../store/selectors/player.selector';
import * as PlayActions from '../../../../../store/actions/player.action';
import {Song} from '../../../../../data-types/common.types';
import {PlayMode} from '../../../../../data-types/store/player-reducer.interface';
import {shuffle} from '../../../../../helpers/slider';

@Component({
  selector: 'app-player',
  templateUrl: './player.component.html',
  styleUrls: ['./player.component.less']
})
export class PlayerComponent implements OnInit {

  @ViewChild('audio', {static: true}) private audio: ElementRef;

  private audioElement: HTMLAudioElement;

  public percent = 0;

  public bufferPercent = 0;

  public songList: Array<Song>;

  public playList: Array<Song>;

  public currentIndex: number;

  public currentSong: Song;

  public playing: boolean;

  // 歌曲的时长
  public duration: number;

  // 当前歌曲的进度
  public currentTime: number;

  // 是否可以播放
  public songReady: boolean;

  // 音量
  public volume = 60;

  // 显示音量面板
  public showVolumePanel = false;

  // 显示播放面板
  public showPlayerPanel = false;

  // 点击的是否是音量面板自身
  public selfClick = false;

  // 绑定 window click 事件
  private winClick: Subscription;

  // 播放模式
  public currentMode: PlayMode;

  // 所有的播放模式
  private readonly modeTypes: Array<PlayMode>;

  // 点击播放模式的次数
  public modeCount = 0;

  constructor(
    private store$: Store<AppStoreModule>,
    @Inject(DOCUMENT) private document: Document
  ) {
    const appStore$ = this.store$.pipe(select(PlaySelector.getPlayer));

    const stateArr = [
      {
        type: PlaySelector.getSongList,
        cb: songList => this.watchSongList(songList)
      },
      {
        type: PlaySelector.getPlayList,
        cb: playList => this.watchPlayList(playList)
      },
      {
        type: PlaySelector.getCurrentIndex,
        cb: currentIndex => this.watchCurrentIndex(currentIndex)
      },
      {
        type: PlaySelector.getPlayMode,
        cb: playMode => this.watchPlayMode(playMode)
      },
      {
        type: PlaySelector.getCurrentSong,
        cb: currentSong => this.watchCurrentSong(currentSong)
      }
    ];

    stateArr.forEach(state => {
      // @ts-ignore
      appStore$.pipe(select(state.type)).subscribe(state.cb);
    });

    this.currentMode = {
      type: 'loop',
      label: '循环'
    };

    this.modeTypes = [
      {type: 'loop', label: '循环'},
      {type: 'random', label: '随机'},
      {type: 'singleLoop', label: '单曲循环'}
    ];
  }

  ngOnInit(): void {
    this.audioElement = this.audio.nativeElement;
  }

  private watchSongList(songList: Array<Song>) {
    this.songList = songList;
  }

  private watchPlayList(playList: Array<Song>) {
    this.playList = playList;
  }

  private watchCurrentIndex(currentIndex: number) {
    this.currentIndex = currentIndex;
  }

  private watchPlayMode(playMode: PlayMode) {
    this.currentMode = playMode;
    if (this.songList) {
      let songList = this.songList.slice(0);

      if (playMode.type === 'random') {
        songList = shuffle(this.songList);
        this.updateCurrentIndex(songList, this.currentSong);
        this.store$.dispatch(PlayActions.SetPlayList({playList: songList}));
      }
    }
  }

  private watchCurrentSong(currentSong: Song) {
    this.currentSong = currentSong;
    if (currentSong) {
      this.duration = currentSong.dt / 1000;
    }
  }

  play() {
    this.playing = true;
    this.audioElement.play();
  }

  public audioCanPlay() {
    this.songReady = true;
    this.play();
  }

  get picUrl(): string {
    return this.currentSong ? this.currentSong.al.picUrl : null;
  }

  // 歌曲改变进度
  public audioTimeUpdate(e: Event) {
    const target = e.target as HTMLAudioElement;
    this.currentTime = target.currentTime;
    this.percent = (this.currentTime / this.duration) * 100;

    // 缓冲区域
    const buffered = this.audioElement.buffered;
    if (buffered.length && this.bufferPercent < 100) {
      this.bufferPercent = (buffered.end(0) / this.duration) * 100;
    }
  }

  // 歌曲播放结束
  public audioPlayEnded(e: Event) {
    this.playing = false;
    if (this.currentMode.type === 'singleLoop') {
      this.loop();
    } else {
      this.onNextClick(this.currentIndex + 1);
    }
  }

  private updateIndex(newIndex: number) {
    this.store$.dispatch(PlayActions.SetCurrentIndex({currentIndex: newIndex}));
    this.songReady = false;
  }

  // 播放暂停
  public onToggleClick() {
    if (!this.currentSong) {
      if (this.playList.length > 0) {
        this.store$.dispatch(PlayActions.SetCurrentIndex({currentIndex: 0}));
        this.songReady = false;
      }
    } else {
      if (this.songReady) {
        this.playing = !this.playing;
        if (this.playing) {
          this.audioElement.play();
        } else {
          this.audioElement.pause();
        }
      }
    }
  }

  // 上一首
  public onPrevClick(prevIndex: number) {
    if (!this.songReady) {
      return;
    }
    if (this.playList.length === 1) {
      this.loop();
    } else {
      const newIndex = prevIndex < 0 ? this.playList.length - 1 : prevIndex;
      this.updateIndex(newIndex);
    }
  }

  // 下一首
  public onNextClick(nextIndex: number) {
    if (!this.songReady) {
      return;
    }
    if (this.playList.length === 1) {
      this.loop();
    } else {
      const newIndex = nextIndex >= this.playList.length ? 0 : nextIndex;
      this.updateIndex(newIndex);
    }
  }

  // 循环播放
  private loop() {
    this.audioElement.currentTime = 0;
    this.play();
  }

  // 音乐进度条加载
  public onPercentChange(percent: number) {
    if (this.currentSong) {
      this.audioElement.currentTime = this.duration * (percent / 100);
    }
  }

  // 控制音量
  public onVolumeChange(percent: number) {
    this.audioElement.volume = percent / 100;
  }

  // 显示/隐藏音量面板
  public onVolumePanelClick() {
    this.onTogglePanel('showVolumePanel');
  }

  // 显示/隐藏播放列表面板
  public onPlayerPanelClick() {
    this.onTogglePanel('showPlayerPanel');
  }

  private onTogglePanel(type: string) {
    this[type] = !this[type];
    if (this.showVolumePanel || this.showPlayerPanel) {
      this.bindDocumentClickListener();
    } else {
      this.unbindDocumentClickListener();
    }
  }

  // 绑定 document 事件
  public bindDocumentClickListener() {
    if (!this.winClick) {
      this.winClick = fromEvent(this.document, 'click').subscribe(() => {
        // 说明点击了播放器以外的区域
        if (!this.selfClick) {
          this.showVolumePanel = false;
          this.showPlayerPanel = false;
          this.unbindDocumentClickListener();
        }
        this.selfClick = false;
      });
    }
  }

  // 解绑 document 事件
  public unbindDocumentClickListener() {
    if (this.winClick) {
      this.winClick.unsubscribe();
      this.winClick = null;
    }
  }

  // 改变播放模式
  public changePlayMode() {
    const tempMode = this.modeTypes[++this.modeCount % 3];
    this.store$.dispatch(PlayActions.SetPlayMode({playMode: tempMode}));
  }

  // 修改当前播放音乐索引
  public updateCurrentIndex(songList: Array<Song>, song: Song) {
    const newIndex = songList.findIndex(item => item.id === song.id);
    this.store$.dispatch(PlayActions.SetCurrentIndex({currentIndex: newIndex}));
  }

  // 直接点击换歌
  public changeSongClick(song: Song) {
    this.updateCurrentIndex(this.playList, song);
  }

}
